"use client"; import { use, useEffect, useMemo, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { ArrowLeft, Save, AlertTriangle, Pencil, X, Copy, Check, Hash, Clock, CalendarClock, FileText, Sparkles, Tags, Braces, FolderGit2, } from "lucide-react"; import { PageHeader } from "@/components/common/page-header"; import { LearningHostProvenance } from "@/components/common/host-badge"; import { EmptyState } from "@/components/common/empty-state"; import { DeleteLearningDangerZone } from "@/components/common/delete-learning-danger-zone"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { reflexio } from "@/lib/reflexio-client"; import { formatTimestamp, truncateId } from "@/lib/format"; import { useRequestHostAttribution } from "@/lib/host-attribution"; import { cn } from "@/lib/utils"; import { statusLabel as status } from "@/lib/status"; import type { UserProfile } from "@/lib/types"; export default function PreferenceDetailPage({ params, }: { params: Promise<{ id: string }>; }) { const { id: rawId } = use(params); const id = decodeURIComponent(rawId); const router = useRouter(); const [profile, setProfile] = useState(null); const [notFound, setNotFound] = useState(false); const [error, setError] = useState(null); const [saving, setSaving] = useState(false); const [deleting, setDeleting] = useState(false); const [editing, setEditing] = useState(false); const [content, setContent] = useState(""); const attribution = useRequestHostAttribution(); useEffect(() => { let cancelled = false; reflexio .getProfileById(id, { includeTombstones: true }) .then((found) => { if (cancelled) return; if (!found) { setNotFound(true); return; } setProfile(found); setContent(found.content); }) .catch((e) => { if (!cancelled) setError(e instanceof Error ? e.message : String(e)); }); return () => { cancelled = true; }; }, [id]); const dirty = useMemo( () => !!profile && profile.content !== content, [profile, content], ); const save = async () => { if (!profile || !dirty) return; setSaving(true); setError(null); try { await reflexio.updateUserProfile( { user_id: profile.user_id, profile_id: profile.profile_id, content, }, ); setProfile({ ...profile, content }); setEditing(false); } catch (e) { setError(e instanceof Error ? e.message : String(e)); } finally { setSaving(false); } }; const remove = async () => { if (!profile) return; setDeleting(true); try { await reflexio.deleteUserProfile( { user_id: profile.user_id, profile_id: profile.profile_id }, ); router.push("/preferences"); } catch (e) { setError(e instanceof Error ? e.message : String(e)); setDeleting(false); } }; const cancelEdit = () => { if (profile) setContent(profile.content); setEditing(false); }; if (notFound) { return (
} />
); } const customEntries = profile?.custom_features ? Object.entries(profile.custom_features).filter( ([, v]) => v !== null && v !== undefined && v !== "", ) : []; return (
{!editing ? ( ) : ( <> )}
} />
{error && (
{error}
)} {profile && (
{truncateId(profile.user_id, 32, 8)} {dirty && ( unsaved changes )}
)}
{editing ? (